const _ = require('lodash');
const Thenjs = require('thenjs');
const logger = require('./lib/logger');
const defaultReqNum = 5000;
const defaultLimit = 50;
const XLSX = require('xlsx');
const fs = require('fs');

function stressManager(name, reqNum, limit, debug) {
    this.name = name;
    this.reqNum = reqNum || defaultReqNum;
    this.limit = limit || defaultLimit;
    this.debug = debug;
}

function stress(handler, limit, callback) {
    if (typeof (limit) === 'function') {
        callback = limit;
        limit = this.limit;
    }
    let stats = { name: this.name, limit: limit, error: 0, sum: 0, '95': 0, 'q/s': 0, 'avg': 0, consumList: [] };
    let indexes = _.range(this.reqNum);
    let self = this;
    self.startTime = Date.now();
    Thenjs.eachLimit(indexes, (cont, value, index) => {
        let startTime = Date.now();
        handler((err, result) => {
            if (self.debug) {
                logger.info(`value:${value}, index:${index}`);
            }
            if (err) {
                logger.error(`${self.name} err, ${err.stack || err}`);
                console.error(`${self.name} err, ${err.stack || err}`);
                stats.error++;
            }
            let endTime = Date.now();
            let useTime = endTime - startTime;
            stats.sum += useTime;
            stats.consumList.push(useTime);
            return cont(null, null);
        });
    }, limit).fin((cont, err, arg) => {
        self.endTime = Date.now();
        stats['q/s'] = self.reqNum * 1000 / (self.endTime - self.startTime);
        stats['avg'] = stats['sum'] / self.reqNum;
        let consumList = _.orderBy(stats['consumList']);
        delete stats['consumList'];
        let startIndex = Math.ceil(self.reqNum * 0.94) - 1;
        let endIndex = Math.ceil(self.reqNum * 0.95);
        consumList = consumList.slice(startIndex, endIndex);
        stats['95'] = consumList.reduce((sum, cur) => sum + cur, 0) / (endIndex - startIndex);
        return callback(err, stats);
    });
}


function stressMany(limitArr, handler, callback) {
    let self = this;
    let stats = [];
    Thenjs.eachSeries(limitArr, (cont, limit) => {
        self.stress(handler, limit, (err, result) => {
            if (err) return callback(err);
            stats.push(result);
            logger.info(JSON.stringify(result));
            console.info(JSON.stringify(result));
            return cont(null, null);
        });
    }).then((cont, arg) => {
        if (stats.length === 0) {
            logger.info(`stats is empty`);
            console.info(`stats is empty`);
            return cont(null, null);
        }
        let out = [];
        for (let stat of stats) {
            out.push({
                '总请求数': self.reqNum || 0,
                '并发量': stat.limit || 0,
                '失败': stat.error || 0,
                '吞吐量': parseInt(stat['q/s']),
                '平均请求响应时间': parseInt(stat.avg),
                '95分位': parseInt(stat['95'])
            });
        }
        exportExcel(out, self.name, self.name, cont);
    }).fin((cont, err, arg) => {
        return callback(err, stats);
    });
}

function safeWrite(path, data, callback) {
    if (!path || !data) {
        return process.nextTick(function () {
            callback(null, data);
        });
    }
    var tmp = path + '.tmp';
    if ('string' != typeof data && !(data instanceof Buffer)) {
        data = JSON.stringify(data);
    }
    fs.writeFile(tmp, data, function (err, arg) {
        if (null == err) {
            return fs.rename(tmp, path, callback);
        }
        callback(err, arg);
    });
}

stressManager.prototype.stress = stress;
stressManager.prototype.stressMany = stressMany;
function exportExcel(data, filename, sheetName, callback) {
    let wb = XLSX.utils.book_new();
    wb.Sheets[sheetName] = XLSX.utils.json_to_sheet(data);
    wb.SheetNames.push(sheetName);
    let buffer = XLSX.write(wb, { type: 'buffer' });
    safeWrite(`./${filename}.xlsx`, buffer, callback);
}

module.exports = stressManager;